using System.Collections.Generic;
using System.Linq;
using System;
using System.Xml;

namespace MyUtils.Performance
{
	class ProfilingNode
	{
		public override string ToString()
		{
			return string.Format("!! ProfilingNode: {0} ({1})", Name, TimeHere.ToSpanString());
		}

		public readonly ProfilingNode Parent;

		public static ProfilingNode CreateRoot()
		{
			return new ProfilingNode(null, "Root");
		}

		ProfilingNode(ProfilingNode parent, string name)
		{
			if (name == null)
			{
				throw new ArgumentNullException("name");
			}
			Parent = parent;
			Name = name;
		}

		public string Name {get; private set;}

		internal TimeSpan _timeHereActual;
		public TimeSpan TimeHere
		{
			get
			{
				if (_timeHereActual == default(TimeSpan))
				{
					return new TimeSpan(SubNodes.Sum(x => x.Value.TimeHere.Ticks));
				}
				return _timeHereActual;
			}
		}

		public string TimeAsString
		{
			get
			{
				return TimeHere.ToSpanString();
			}
		}

//		public TimeSpan TimeWithSub
//		{
//			get
//			{
//				lock (SubNodes)
//				{
//					return new TimeSpan(SubNodes.Sum(x => x.Value.TimeWithSub.Ticks)) + TimeHere;
//				}
//			}
//		}

		readonly Dictionary<string, ProfilingNode> _subNodes = new Dictionary<string, ProfilingNode>(16);

		Dictionary<string, ProfilingNode> SubNodes
		{
			get { return _subNodes; }
		}

		public IEnumerable<ProfilingNode> SubNodesOrdered
		{
			get { return _subNodes.Values.OrderByDescending(x => x.TimeHere); }
		}

		public ProfilingNode SubNode(string name)
		{
			ProfilingNode node;
			lock (_subNodes)
			{
				if (!_subNodes.TryGetValue(name, out node))
				{
					_subNodes[name] = node = new ProfilingNode(this, name);
				}
			}
			return node;
		}

		internal void FillLog(XmlDocument doc, XmlNode parentNode)
		{
			var me = doc.CreateElement("S");

			var name = doc.CreateAttribute("N");
			name.Value = Name;
			me.Attributes.Append(name);

			var val = doc.CreateAttribute("T");
			val.Value = TimeHere.ToSpanString();
			me.Attributes.Append(val);

//			var valWithSub = doc.CreateAttribute("TimeWithSub");
//			valWithSub.Value = TimeWithSub.ToSpanString();
//			me.Attributes.Append(valWithSub);

			parentNode.AppendChild(me);

			foreach (var subNode in _subNodes.Values)
			{
				subNode.FillLog(doc, me);
			}
		}
	}
}